package com.louis.bean.impl;

import com.louis.annotation.Bean;
import com.louis.annotation.Di;
import com.louis.bean.ApplicationContext;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * @author XRY
 * @date 2023年06月26日16:39
 */
public class AnnotationApplicationContext implements ApplicationContext {
    //模拟IOC，创建map集合，放置bean对象
    private Map<Class, Object> beanFactory = new HashMap<>();
    private static String rootPath;

    //返回对象
    @Override
    public Object getBean(Class clazz) {
        return beanFactory.get(clazz);
    }

    //设置包扫描规则
    //当前包及其子包，将带有@Bean注解的类通过反射实现实例化
    public AnnotationApplicationContext(String basePackage){
        //扫描路径
        //1、将.替换成\
        String packagePath = basePackage.replaceAll("\\.", "\\\\");
        //2、获取包的绝对路径，编译之后的路径
        try {
            Enumeration<URL> urls = Thread.currentThread().getContextClassLoader().getResources(packagePath);
            while(urls.hasMoreElements()){
                URL url = urls.nextElement();
                //转码
                String filePath = URLDecoder.decode(url.getFile(), "utf-8");
                //获取包前面路劲部分，字符串截取
                rootPath = filePath.substring(0, filePath.length() - packagePath.length());
//                System.out.println("filePath = " + filePath);
                //根据获得的路径进行包扫描
                loadBean(new File(filePath));
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        //属性注入
        loadDi();
    }

    /**
     * 包扫描过程，进行实例化
     * @param file
     */
    private void loadBean(File file) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //1、判断当前路径下的东西是否是文件夹，如果不是就不需要继续往下查找
        if(file.isDirectory()){
            //2、获取文件夹里面所有内容
            File[] childFiles = file.listFiles();
            //3、判断文件夹里面为空，直接返回
            if(childFiles == null || childFiles.length == 0){
                return;
            }
            //4、如果文件夹里面不为空，遍历文件夹中所有内容
            for(File child:childFiles){
                //4.1遍历得到每个File对象，继续判断，如果是文件夹，递归
                if(child.isDirectory()){
                    //递归
                    loadBean(child);
                }else{
                    //4.2遍历得到的File对象不是文件夹
                    //4.3得到包路径+类名称部分
                    String packagePath = child.getAbsolutePath().substring(rootPath.length() - 1);
                    //4.4判断当前文件类型是否为.class，如果是，将路径中的\替换为.并将.class文件去掉
                    if(packagePath.contains(".class")){
                        String allName = packagePath.replaceAll("\\\\", "\\.").replaceAll(".class", "");
                        //4.5判断类上面是否有注解@Bean,如果有进行实例化过程
                        //4.5.1获取类的class
                        Class<?> clazz = Class.forName(allName);
                        //4.5.2判断不是interface
                        if(!clazz.isInterface()){
                            Bean annotation = clazz.getAnnotation(Bean.class);
                            if(annotation != null){
                                //4.5.3实例化
                                Object instance = clazz.getConstructor().newInstance();
                                //4.7把对象实例化之后，放到map集合beanFactory
                                //4.7.1判断当前类如果有接口，让接口class作为map的key，如果没有接口将自己的class作为key
                                if(clazz.getInterfaces().length > 0){
                                    beanFactory.put(clazz.getInterfaces()[0], instance);
                                }else{
                                    beanFactory.put(clazz,instance);
                                }
                            }
                        }
                    }
                }

            }

        }


    }

//    public static void main(String[] args) {
//        new AnnotationApplicationContext("com.louis");
//    }

    //属性注入
    private void loadDi(){
        //实例化的对象都在beanFactory的map集合里
        //1、遍历beanFactory的map集合,entrySet()用来获取到对象的集合
        Set<Map.Entry<Class, Object>> entries = beanFactory.entrySet();
        for (Map.Entry<Class, Object> entry : entries) {
            //2、获取map集合中每个对象（value)，每个对象获取到属性
            Object value = entry.getValue();
            //获取对象Class
            Class<?> clazz = value.getClass();
            //获取每个对象中的属性
            Field[] declaredFields = clazz.getDeclaredFields();
            //3、遍历得到的每个对象属性的数组，得到每个属性
            for (Field field : declaredFields) {
                //4、判断属性上是否有@Di注解，
                Di annotation = field.getAnnotation(Di.class);
                if(annotation != null){
                    //如果是私有属性可以设置一个值
                    field.setAccessible(true);
                    //有Di注解就把对象进行设置(注入）
                    try {
                        field.set(value, beanFactory.get(field.getType()));
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException(e);
                    }
                }

            }


        }
    }


}
